import asyncio
import base64
import json
import random
import time
import uuid
from datetime import datetime

import aiohttp
from aiohttp_socks import ProxyConnector
from loguru import logger

from common import config, utils
from core.dashboard import dashboard


class Session:
    def __init__(self, sid, username, password, user_id, device_id, user_agent, proxy_ip, proxy_port, proxy_username, proxy_password):
        self.sid = sid
        self.username = username
        self.password = password
        self.user_id = user_id
        self.device_id = device_id
        self.user_agent = user_agent
        self.proxy_ip = proxy_ip
        self.proxy_port = proxy_port
        self.proxy_username = proxy_username
        self.proxy_password = proxy_password
        self.update_at = datetime.now()
        self.status = "init"
        self.desc = "正在连接中"
        self.source = "100%"
        self.proxy_session = None
        self.ws_client = None
        self.ping_monitor = None
        self.recv_monitor = None
    async def connect(self):
        try:
            await asyncio.sleep(random.uniform(10, 300))
            self.reset_session_connection()
            proxy_url = f"socks5://{self.proxy_username}:{self.proxy_password}@{self.proxy_ip}:{self.proxy_port}"
            self.proxy_session = aiohttp.ClientSession(connector=ProxyConnector.from_url(proxy_url, ssl=False))
            self.ws_client = await self.proxy_session.ws_connect(url=config.WSS_SERVER_URL,
                            headers={
                                'User-Agent': self.user_agent,
                                'Accept-Language': 'zh-CN,zh;q=0.9',
                                'Cache-Control': 'no-cache',
                            },
                            timeout=30,
                            heartbeat=30
            )
            self.ping_monitor = asyncio.create_task(self.start_ping_monitor())
            self.recv_monitor = asyncio.create_task(self.start_recv_monitor())
            monitor_task = [self.ping_monitor, self.recv_monitor]
            await asyncio.gather(*monitor_task)
            dashboard.append_log(f"用户【{self.username}】使用代理【{utils.mark_ip_field(self.proxy_ip)}:{self.proxy_port}】连接成功")
        except Exception as e:
            if utils.check_is_invalid_proxy_exception(str(e)):
                self.handle_invalid_connection_exception(e)
            else:
                self.handle_connection_exception(e, "connect")
    def reset_session_connection(self):
        self.status = "init"
        self.desc = "正在连接中"
        self.proxy_session = None
        self.ws_client = None
        self.cancel_ping_monitor()
        self.cancel_recv_monitor()
    def close_proxy_session(self):
        try:
            if self.proxy_session is not None:
                self.proxy_session.close()
        except Exception as e:
            logger.error(f"关闭代理连接异常：{e}")
    def cancel_ping_monitor(self):
        try:
            if self.ping_monitor is not None and not self.ping_monitor.cancelled():
                self.ping_monitor.cancel()
        except Exception as e:
            logger.error(f"取消ping monitor异常：{e}")
    async def start_ping_monitor(self):
        try:
            while True:
                await asyncio.sleep(config.PING_CHECK_INTERVAL)
                await self.ws_client.send_str(json.dumps({"id": str(uuid.uuid4()), "version": "1.0.0", "action": "PING", "data": {}}))
                self.update_at = datetime.now()
        except Exception as e:
            self.handle_connection_exception(e, "ping_monitor")
    def cancel_recv_monitor(self):
        try:
            if self.recv_monitor is not None and not self.recv_monitor.cancelled():
                self.recv_monitor.cancel()
        except Exception as e:
            logger.error(f"取消recv monitor异常：{e}")
    async def start_recv_monitor(self):
        if self.ws_client is not None:
            try:
                async for msg in self.ws_client:
                    message = json.loads(msg.data)
                    print(f"接收到消息:{message}")
                    if message.get("action") == "AUTH":
                        auth_response = {
                            "id": message["id"],
                            "origin_action": "AUTH",
                            "result": {
                                "browser_id": self.device_id,
                                "user_id": self.user_id,
                                "user_agent": self.user_agent,
                                "timestamp": int(time.time()),
                                "device_type": "desktop",
                                "version": "4.29.0"
                            }
                        }
                        print(json.dumps(auth_response))
                        await self.ws_client.send_str(json.dumps(auth_response))
                        self.status = "success"
                        dashboard.append_log(f"用户【{self.username}】节点【{utils.mark_ip_field(self.proxy_ip)}:{self.proxy_port}】激活成功")
                        dashboard.update_row(self.sid, self.status, f"节点激活成功,耗时{(datetime.now() - self.update_at).total_seconds():.2f}s,挖矿中!")
                    elif message.get("action") == "PONG":
                        pong_response = {"id": message["id"], "origin_action": "PONG"}
                        print(f"发送消息:{pong_response}")
                        await self.ws_client.send_str(json.dumps(pong_response))
                        dashboard.append_log(f"用户【{self.username}】节点【{utils.mark_ip_field(self.proxy_ip)}:{self.proxy_port}】存活检测成功")
                        dashboard.update_row(self.sid, self.status, "节点激活成功, 挖矿中！")
                    elif message.get("action") == "HTTP_REQUEST":
                        response = await self.handle_http_request(message)
                        print(json.dumps(response))
                        await self.ws_client.send_str(json.dumps(response))
                    else:
                        print(msg.data)
                        dashboard.append_log(f"用户【{self.username}】节点【{self.proxy_ip}:{self.proxy_port}】接收到消息:{msg.data}")
                self.update_at = datetime.now()
            except Exception as e:
                self.handle_connection_exception(e, "recv_monitor")

    @staticmethod
    async def handle_http_request(message):
        try:
            params = message['data']
            request_options = {
                'headers': params['headers'],
            }
            async with aiohttp.ClientSession() as session:
                if 'body' in params:
                    fetch_url = f"data:application/octet-stream;base64,{params['body']}"
                    async with session.get(fetch_url) as fetch_resp:
                        request_options['data'] = await fetch_resp.read()

                async with session.request(
                        method=params['method'],
                        url=params['url'],
                        **request_options
                ) as response:
                    headers = {
                        "connection": response.headers.get("Connection", "keep-alive"),
                        "content-length": response.headers.get("Content-Length", ""),
                        "content-type": response.headers.get("Content-Type", ""),
                        "date": response.headers.get("Date", ""),
                        "etag": response.headers.get("ETag", ""),
                        "keep-alive": response.headers.get("Keep-Alive", ""),
                        "x-powered-by": response.headers.get("X-Powered-By", "")
                    }

                    body = await response.read()
                    base64_body = base64.b64encode(body).decode('utf-8')

                    return {
                        'id': message['id'],
                        'origin_action': 'HTTP_REQUEST',
                        'result': {
                            'url': str(response.url),
                            'status': response.status,
                            'status_text': response.reason,
                            'headers': headers,
                            'body': base64_body,
                        }
                    }
        except Exception as e:
            print(f"处理HTTP_REQUEST消息异常:{e}")
            logger.error(f"处理HTTP_REQUEST消息异常:{e}")
            return {
                'id': message['id'],
                'origin_action': 'HTTP_REQUEST',
                'result': {
                    'url': message['data']['url'],
                    'status': 400,
                    'status_text': 'Bad Request',
                    'headers': {},
                    'body': '',
                }
            }

    def handle_invalid_connection_exception(self, e):
        self.status = "invalid"
        self.desc = str(e)
        dashboard.append_log(f"用户【{self.username}】节点【{utils.mark_ip_field(self.proxy_ip)}:{self.proxy_port}】连接无效:{e}")
        dashboard.update_row(self.sid, self.status, "节点无效")
        logger.error(f"用户【{self.username}】节点【{utils.mark_ip_field(self.proxy_ip)}:{self.proxy_port}】连接无效:{e}")
    def handle_connection_exception(self, e, anchor):
        self.status = "failure"
        self.desc = str(e)
        dashboard.append_log(f"用户【{self.username}】节点【{utils.mark_ip_field(self.proxy_ip)}:{self.proxy_port}】连接异常:{e}")
        dashboard.update_row(self.sid, self.status, f"节点连接异常,即将重新连接：{e}")
        logger.error(f"锚点:{anchor},用户【{self.username}】节点【{utils.mark_ip_field(self.proxy_ip)}:{self.proxy_port}】连接异常:{e}")
    def is_healthy(self):
        if self.status != 'success' and self.status != 'invalid':
            return False
        if (datetime.now() - self.update_at).total_seconds() > config.PING_CHECK_INTERVAL + 30:
            return False
        return True

